using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;

using Microsoft.DirectX;
using TTDotNet.Util;
using TTDotNet.Engine.Terrain;
using TTDotNet.Engine.ThreeDEngine;

namespace TTDotNet.Engine.Create.Tools
{
    public enum LandscapingType { None, Raise, Lower, Level }

    
    public class LandscapingTool : Tool 
    {
        private LandscapingType tool;
        //private TileCorner mouseDownAt;

        private InstancedMesh vertexCube;
        private bool cubeShown;
        private bool lmbGoneDown;

        private Dictionary<Coordinate, int> vertexCubes;

        public LandscapingTool(Game game) : base(game, ToolType.Landscaping)
        {
            tool = LandscapingType.None;
            //vertexCube = Game.meshManager.CreateMesh("data/vertexCube.x", Vector3.Empty);
        }

        public LandscapingType ActiveTool
        {
            get { return tool; }
            set {
                if (value == LandscapingType.None) {
                    //vertexCube.mesh.AddNewInstance(new Vector3()); 
                    cubeShown = false; 
                }
                else { 
                    if (!cubeShown) { 
                        //Game.myScene.AddObject(vertexCube); 
                        cubeShown = true; 
                    } 
                }
                tool = value;
            }
        }

        public override void MouseDown(Point mouseLocation, GridPickData data)
        {
            MouseHandler(data.CornerVertex, true);
        }

        public override void MouseUp(Point mouseLocation, GridPickData data)
        {
            MouseHandler(data.CornerVertex, false);
        }

        public void MouseHandler(Coordinate vert, bool lmbDown)
        {
            if (tool == LandscapingType.Lower || tool == LandscapingType.Raise)
            {
                if (lmbDown)
                {
                    lmbGoneDown = true;
                }
                else {

                    if (lmbGoneDown)
                    {
                        int change = 2;

                        if (tool == LandscapingType.Lower) change *= -1;

                        List<CornerHeight> heights = new List<CornerHeight>();

                        if (vert.X > -1 && vert.Z > -1)
                        {
                            heights.Add(new CornerHeight(vert.X, vert.Z, Game.gameWorld.Map.GetCornerHeight(vert.X, vert.Z) + change));
                        

                        CornerHeight ccc = heights[0];

                        if (heights[0].Height < 0)
                        {
                            ccc.Height = 0;
                            heights[0] = ccc;
                        }

                        List<CornerHeight> allRejigged = new List<CornerHeight>();

                        bool rejigCheck = true;
                        bool validEdit = true;
                        int layer = 0;
                        int newX, newZ;
                        List<CornerHeight> toCheck = new List<CornerHeight>();
                        Map map = Game.gameWorld.Map;

                        if (heights[0].Height / 2 <= Math.Min(Math.Min(heights[0].X, heights[0].Z), Math.Min(map.Width - heights[0].X, map.Length - heights[0].Z)) && CheckCanLandscape(heights[0]))
                        {
                            map.EditLand(heights[0]);
                        }
                        else rejigCheck = false;
                        

                        while (rejigCheck)
                        {
                            layer++;
                            rejigCheck = false;

                            toCheck.Clear();

                            //Add first one;
                            newX = vert.X;
                            newZ = vert.Z + layer;

                            if (map.CornerCoordExists(newX, newZ))
                                toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                            //else validEdit = false;

                            //Add aboves.
                            for (int i = 1; i < layer; i++)
                            {
                                newX = vert.X - i;
                                newZ = vert.Z + layer - i;

                                //add left
                                if (map.CornerCoordExists(newX, newZ))
                                    toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                                //else validEdit = false;

                                newX = vert.X + i;
                                //add right
                                if (map.CornerCoordExists(newX, newZ))
                                    toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                                //else validEdit = false;

                                if (!validEdit) break;
                            }

                            //Add middles

                            newX = vert.X - layer;
                            newZ = vert.Z;

                            if (map.CornerCoordExists(newX, newZ))
                                toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                            //else validEdit = false;

                            newX = vert.X + layer;

                            if (map.CornerCoordExists(newX, newZ))
                                toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                            //else validEdit = false;


                            //Add belows.
                            for (int i = 1; i < layer; i++)
                            {
                                if (!validEdit) break;

                                newX = vert.X - i;
                                newZ = vert.Z - layer + i;

                                if (map.CornerCoordExists(newX, newZ))
                                    toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                                //else validEdit = false;

                                newX = vert.X + i;

                                if (map.CornerCoordExists(newX, newZ))
                                    toCheck.Add(new CornerHeight(newX, newZ, Game.gameWorld.Map.GetCornerHeight(newX, newZ)));
                                //else validEdit = false;
                            }


                            //Add last one;
                            if (map.CornerCoordExists(vert.X, vert.Z - layer))
                                toCheck.Add(new CornerHeight(vert.X, vert.Z - layer, Game.gameWorld.Map.GetCornerHeight(vert.X, vert.Z - layer)));
                            //else validEdit = false;

                            CornerHeight store = new CornerHeight(0, 0, 0);

                            int width = Game.gameWorld.Map.Width;
                            int length = Game.gameWorld.Map.Length;

                            List<CornerHeight> rejigged = new List<CornerHeight>();

                            if (validEdit)
                            {
                                foreach (CornerHeight ch in toCheck)
                                {
                                    store = ReJigVertex(ch, change);

                                    if (store.Height != int.MinValue)
                                    {
                                        rejigged.Add(store);
                                    }
                                }

                                foreach (CornerHeight ch in rejigged)
                                {
                                    int min = Math.Min(Math.Min(ch.X, ch.Z), Math.Min(width - ch.X, length - ch.Z));
                                    bool can = CheckCanLandscape(ch);

                                    if (ch.Height / 2 > min || !can )
                                    {
                                        validEdit = false;
                                        break;
                                    }
                                }

                                if (validEdit)
                                {
                                    foreach (CornerHeight ch in rejigged)
                                    {
                                        rejigCheck = true;
                                        map.EditLand(ch);
                                    }
                                }

                            }

                        }

                        if (!validEdit) Game.gameWorld.Map.CancelEdit();

                        //heights.AddRange(allRejigged);

                        //bool landscapeOK = true;

                        //foreach (TileCorner tc in list)
                        //{
                        //    if (!CheckCanLandscape(tc)) { landscapeOK = false; break; }
                        //}

                        //if (!landscapeOK) { list = new List<TileCorner>(); }

                        //return list;

                    }

                        lmbGoneDown = false;
                    }

                }
            }
            else if(tool == LandscapingType.Level)
            {
                //if (lmbDown)
                //{
                //    if (mouseDownAt == null)
                //    {
                //        mouseDownAt = new TileCorner(new Corner(corner.Corner.Height, corner.Corner.Direction), corner.Coord);
                //    }
                //}
                //else
                //{   
                //    if (mouseDownAt != null)
                //    {
                //        List<TileCorner> all = LevelledTileCorners(mouseDownAt, corner);

                //        mouseDownAt = null;

                //        return all;
                //    }
                //}
            }
        }

        private bool CheckCanLandscape(CornerHeight ch)
        {
            List<Coordinate> shared = Game.gameWorld.Map.GetSharedTiles(ch);
            Map map = Game.gameWorld.Map;

            foreach (Coordinate coord in shared)
            {
                if (map.GetLandType(coord.X, coord.Z) == LandType.Road || map.IsBuildingOnTile(coord.X, coord.Z))
                {
                    return false;
                }
            }

            return true;
        }

        private void RemoveVertexCubes()
        {
            foreach (Coordinate c in vertexCubes.Keys)
            {
                //Game.myScene.RemoveObject(vertexCubes[c]);
            }
            vertexCubes.Clear();
        }

        private void SetupVertexCubes(List<TileCorner> highlighted)
        {
            List<Coordinate> coords = GetUniqueCoords(highlighted);

            List<Coordinate> removedKeys = new List<Coordinate>();

            foreach (Coordinate c in vertexCubes.Keys)
            {
                if(!coords.Contains(c)) {
                    //Game.myScene.RemoveObject(vertexCubes[c]);
                    removedKeys.Add(c);
                }
            }

            foreach (Coordinate removed in removedKeys) { vertexCubes.Remove(removed); }

            foreach (TileCorner tc in highlighted)
            {
                if (vertexCubes.ContainsKey(tc.Coord))
                {

                }
                else
                {
                    //AJMesh vc = new AJMesh(vertexCube);
                    ////vc.SetPosition(VertexCubePosition(tc));
                    //Game.myScene.AddObject(vc);
                    //vertexCubes[tc.Coord] = vc;
                }
            }
        }

        private List<Coordinate> GetUniqueCoords(List<TileCorner> highlighted)
        {
            List<Coordinate> uniques = new List<Coordinate>();

            foreach (TileCorner tc in highlighted)
            {
                if (!uniques.Contains(tc.Coord))
                {
                    uniques.Add(tc.Coord);
                }
            }

            return uniques;
        }

        public Vector3 VertexCubePosition(TileCorner corner)
        {
            Vector3 position = new Vector3();

            int x = corner.Coord.X;
            int z = corner.Coord.Z;

            switch (corner.Corner.Direction)
            {
                case Direction.NE: position = new Vector3(x + 0.5f, Game.gameWorld.Map.GetTile(x,z).GetCorner(Direction.NE) / Game.heightScale, z + 0.5f); break;
                case Direction.SE: position = new Vector3(x + 0.5f, Game.gameWorld.Map.GetTile(x, z).GetCorner(Direction.SE) / Game.heightScale, z - 0.5f); break;
                case Direction.SW: position = new Vector3(x - 0.5f, Game.gameWorld.Map.GetTile(x, z).GetCorner(Direction.SW) / Game.heightScale, z - 0.5f); break;
                case Direction.NW: position = new Vector3(x - 0.5f, Game.gameWorld.Map.GetTile(x, z).GetCorner(Direction.NW) / Game.heightScale, z + 0.5f); break;
            }

            return position;
        }

        private List<TileCorner> LevelledTileCorners(TileCorner start, TileCorner finish) 
        {
            List<TileCorner> all = new List<TileCorner>();

            Point finishPoint = GetXZFinish(start, finish);

            int xFinish = finishPoint.X;
            int zFinish = finishPoint.Y;

            int xchange = 1;
            int zchange = 1;

            if (start.Coord.X > xFinish) 
            { 
                xchange = -1; 
            }

            if (start.Coord.Z > zFinish)
            {
                zchange = -1;
            }

            int xStart = start.Coord.X;
            int zStart = start.Coord.Z;

            int width = Game.gameWorld.Width;
            int length = Game.gameWorld.Length;

            if (xStart > -1 && xStart < width && xFinish > -1 && xFinish < width)
            {
                if (zStart > -1 && zStart < length && zFinish > -1 && zFinish < length)
                {

                    for (int z = zStart; (z * zchange) <= (zFinish * zchange); z += zchange)
                    {

                        for (int x = xStart; (x * xchange) <= (xFinish * xchange); x += xchange)
                        {
                            all.Add(new TileCorner(new Corner(start.Corner.Height, start.Corner.Direction), new Coordinate(x, z)));
                        }

                    }
                }
            }

            return all;
        }

        private Point GetXZFinish(TileCorner start, TileCorner finish)
        {
            Point xzChange = new Point(0,0);

            // Formularise for speed!
            // Ugly double switch
            #region Ugly Switch
            switch (start.Corner.Direction)
            {
                case Direction.NE:
                    {
                        #region NE ->
                        switch (finish.Corner.Direction)
                        {
                            case Direction.NE:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.SE:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = -1;
                                    break;
                                }
                            case Direction.SW:
                                {
                                    xzChange.X = -1;
                                    xzChange.Y = -1;
                                    break;
                                }
                            case Direction.NW:
                                {
                                    xzChange.X = -1;
                                    xzChange.Y = 0;
                                    break;
                                }
                        }
                        #endregion
                        break;
                    }
                case Direction.SE:
                    {
                        #region SE ->
                        switch (finish.Corner.Direction)
                        {
                            case Direction.NE:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 1;
                                    break;
                                }
                            case Direction.SE:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.SW:
                                {
                                    xzChange.X = -1;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.NW:
                                {
                                    xzChange.X = -1;
                                    xzChange.Y = 1;
                                    break;
                                }
                        }
                        #endregion
                        break;
                    }
                case Direction.SW:
                    {
                        #region SW ->
                        switch (finish.Corner.Direction)
                        {
                            case Direction.NE:
                                {
                                    xzChange.X = 1;
                                    xzChange.Y = 1;
                                    break;
                                }
                            case Direction.SE:
                                {
                                    xzChange.X = 1;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.SW:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.NW:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 1;
                                    break;
                                }
                        }
                        #endregion
                        break;
                    }
                case Direction.NW:
                    {
                        #region NW ->
                        switch (finish.Corner.Direction)
                        {
                            case Direction.NE:
                                {
                                    xzChange.X = 1;
                                    xzChange.Y = 0;
                                    break;
                                }
                            case Direction.SE:
                                {
                                    xzChange.X = 1;
                                    xzChange.Y = -1;
                                    break;
                                }
                            case Direction.SW:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = -1;
                                    break;
                                }
                            case Direction.NW:
                                {
                                    xzChange.X = 0;
                                    xzChange.Y = 0;
                                    break;
                                }
                        }
                        #endregion
                        break;
                    }
            } 
            #endregion

            return new Point(finish.Coord.X + xzChange.X, finish.Coord.Z + xzChange.Y);
        }

        private CornerHeight ReJigVertex(CornerHeight source, int upDown)
        {
            List<CornerHeight> neighbours = CornerHeight.GetVertexNeighbours(source);

            foreach (CornerHeight neighbour in neighbours)
            {
                if (Math.Abs(source.Height - neighbour.Height) > 2)
                {
                    source.SetHeight(source.Height + upDown);
                    if (source.Height < 0) source.SetHeight(0);
                    return source;
                }
            }

            source.SetHeight(int.MinValue);
            return source;
        }

    }
}
